package awesomehttp

import (
	"errors"
	"fmt"
	"gitee.com/jjawesomejj/awesomehttp/system/helper"
	"gitee.com/jjawesomejj/awesomehttp/system/httpContext"
	"net/http"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"strconv"
	"strings"
)

const REQUEST_METHOD_POST = "POST"
const REQUEST_METHOD_GET = "GET"
const REQUEST_METHOD_ANY = "ANY"
const REQUEST_METHOD_HEAD = "HEAD"
const REQUEST_METHOD_PUT = "PUT"
const REQUEST_METHOD_OPTIONS = "OPTIONS"
const REQUEST_METHOD_PATCH = "PATCH"

type Route struct {
	Router         string
	RunFun         func(ctx *httpContext.HttpContext) interface{}
	RequestMethods []string
	RequestMethod  string
	Middlewares    []Middleware
	RouterReg      map[string]string
}

func (route *Route) Match(url string, requestMethod string) (isMatch bool, matchRouteParams map[string][]string) {
	routeParams := make(map[string][]string)
	url = strings.ReplaceAll("/"+url, "//", "/")
	if route.CheckRequestMethod(requestMethod) == false {
		return false, nil
	}
	if strings.Index(route.Router, "{") == -1 {

		if strings.ToLower(route.Router) == strings.ToLower(url) {
			return true, nil
		} else {
			return false, nil
		}
		//正则匹配
	} else {
		routeList := splitUrl(route.Router)
		requestUrlList := splitUrl(url)
		if len(routeList) != len(requestUrlList) {
			return false, nil
		}
		r, err := regexp.Compile("{(.*?)}")
		if err != nil {
			return false, nil
		}
		for index, itemRoute := range routeList {
			match := r.FindStringSubmatch(itemRoute)
			if len(match) == 0 && strings.ToLower(itemRoute) != strings.ToLower(requestUrlList[index]) {
				return false, nil
			}
			if len(match) != 0 {
				regName := match[1]
				if len(match) != 2 {
					return false, nil
				}
				if route.RouterReg != nil {
					if route.RouterReg != nil {
						if reg, ok := route.RouterReg[regName]; ok {
							r, err := regexp.Compile(reg)
							if err != nil {
								return false, nil
							}
							routeParamsMatch := r.FindStringSubmatch(requestUrlList[index])
							if len(routeParamsMatch) == 0 {
								return false, nil
							}
							routeParams[regName] = routeParamsMatch
						}
					}
				} else {
					routeParams[regName] = []string{requestUrlList[index]}
				}
			}
		}
		return true, routeParams
		//match := r.FindStringSubmatch(route.Router)
		//for index,value:=range routeList{
		//	if value {
		//
		//	}
		//}
	}
}
func splitUrl(url string) []string {
	url = "/" + url
	url = strings.ReplaceAll(url, "//", "/")
	urlParams := strings.Split(url, "/")
	return urlParams
}

func (route *Route) CheckRequestMethod(method string) bool {
	if route.RequestMethod == "" && route.RequestMethods == nil {
		return false
	}
	if route.RequestMethods != nil {
		for _, value := range route.RequestMethods {
			if strings.ToLower(value) == strings.ToLower(method) || value == REQUEST_METHOD_ANY {
				return true
			}
		}
	}
	if strings.ToLower(route.RequestMethod) == strings.ToLower(method) || route.RequestMethod == REQUEST_METHOD_ANY {
		return true
	}

	return false
}
func getNextMiddlewareFun(ctx *httpContext.HttpContext, middleware []Middleware, index int, runFun func(ctx *httpContext.HttpContext) interface{}) func(ctx2 *httpContext.HttpContext) interface{} {
	if len(middleware) == index {
		return func(ctx2 *httpContext.HttpContext) interface{} {
			ctx.ResponseContent = runFun(ctx)
			return nil
		}
	}
	if len(middleware) < index+1 {
		return nil
	} else {
		callMiddleware := middleware[index]
		index++
		return func(ctx2 *httpContext.HttpContext) interface{} {
			return callMiddleware.Handle(ctx, getNextMiddlewareFun(ctx, middleware, index, runFun))
		}
	}
}

func (route *Route) Call(ctx *httpContext.HttpContext) interface{} {
	//调用中间件
	if route.Middlewares != nil && len(route.Middlewares) > 0 {
		route.Middlewares[0].Handle(ctx, getNextMiddlewareFun(ctx, route.Middlewares, 1, route.RunFun))
	} else {
		ctx.ResponseContent = route.RunFun(ctx)
	}
	//调用控制器
	return ctx.ResponseContent
}

type routeTmpParams struct {
	Middlewares []Middleware
	Prefix      string
}
type HttpServerObj struct {
	IpAddress        string
	Port             float64
	addr             *string
	modules          map[string]func() HttpHandler
	ResourcePath     string
	router           []Route
	NotFindAction    func(w http.ResponseWriter, r *http.Request)
	ErrAction        func(w http.ResponseWriter, r *http.Request, err error)
	routeTmpParams   routeTmpParams
	GlobalMiddleware []Middleware
}
type HttpServer interface {
}

func MethodExist(obj interface{}, fun string) bool {
	return reflect.ValueOf(obj).MethodByName(fun).IsValid()
}

func (server *HttpServerObj) Init() {
	if server.router == nil {
		server.router = make([]Route, 0)
	}
	if server.ErrAction == nil {
		server.ErrAction = func(w http.ResponseWriter, r *http.Request, err error) {
			fmt.Println(helper.PrintStackTrace(err))
			w.WriteHeader(500)
			w.Header().Add("Access-Control-Allow-Origin", "*") //允许访问所有域
			w.Write([]byte(helper.JsonEncode(map[string]interface{}{
				"code": 500,
				"msg":  "awesomeTask 无法处理此请求",
			})))
		}
	}
	if server.NotFindAction == nil {
		server.NotFindAction = func(w http.ResponseWriter, r *http.Request) {
			w.WriteHeader(404)
			w.Write([]byte(helper.JsonEncode(map[string]interface{}{
				"code": 404,
				"msg":  "无法处理此请求",
			})))
		}
	}
}
func (server *HttpServerObj) SetGlobalMiddleware(middleware Middleware) {
	if server.GlobalMiddleware == nil {
		server.GlobalMiddleware = make([]Middleware, 0)
	}
	server.GlobalMiddleware = append(server.GlobalMiddleware, middleware)
}
func (server *HttpServerObj) handler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Access-Control-Allow-Origin", "*") //允许访问所有域
	w.Header().Add("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,PATCH,OPTIONS")
	defer func() {
		if err := recover(); err != nil {
			server.ErrAction(w, r, err.(error))
			return
		}
	}()
	url := r.URL.String()
	if url == "/" || url == "" {
		url = "/index.html"
	}
	asbPath := GetCurrentRunningPath() + "/" + server.ResourcePath + "/" + url
	asbPath = strings.ReplaceAll(asbPath, "//", "/")
	if CheckFileIsExist(asbPath) {
		http.ServeFile(w, r, asbPath)
	} else {
		route := strings.Split(r.URL.String(), "?")[0]
		moduleInfo := strings.Split(route, "/")
		action := moduleInfo[len(moduleInfo)-1]
		module := strings.ReplaceAll(route, action, "")
		action = strings.Title(action)
		requestUrl := strings.ReplaceAll(module+"/"+action, "//", "/")
		for _, registerRoute := range server.router {
			isMatch, routeParams := registerRoute.Match(requestUrl, r.Method)
			if isMatch {
				runContext := httpContext.GetHttpContext(w, r)
				runContext.RouteParams = routeParams
				registerRoute.Call(runContext)
				return
			}
		}
		server.NotFindAction(w, r)
	}
}

func CheckFileIsExist(filename string) bool {
	var exist = true
	if _, err := os.Stat(filename); os.IsNotExist(err) {
		exist = false
	}
	return exist
}
func GetCurrentRunningPath() string {
	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		panic(err.Error())
	}
	return strings.Replace(dir, "\\", "/", -1)
}
func (httpServerObj *HttpServerObj) Listen() HttpServer {
	httpServerObj.addr = helper.BuildAddr(httpServerObj.IpAddress, httpServerObj.Port)
	httpServerObj.Init()
	http.HandleFunc("/", httpServerObj.handler)
	fmt.Println("服务启动成功:" + httpServerObj.IpAddress + ":" + strconv.Itoa(int(httpServerObj.Port)))
	go func() {
		err := http.ListenAndServe(httpServerObj.IpAddress+":"+strconv.Itoa(int(httpServerObj.Port)), nil)
		if err != nil {
			panic("http服务启动失败:" + err.Error())
		}
	}()
	return httpServerObj
}

func (httpServerObj *HttpServerObj) ListenWith(serverMux *http.ServeMux) HttpServer {
	httpServerObj.addr = helper.BuildAddr(httpServerObj.IpAddress, httpServerObj.Port)
	httpServerObj.Init()
	serverMux.HandleFunc("/", httpServerObj.handler)
	return httpServerObj
}

func (httpServerObj *HttpServerObj) AddRoute(route Route) error {
	if route.RunFun == nil {
		fmt.Println("Add route fail please init [RunFun]")
		return errors.New("Add route fail please init [RunFun]")
	}
	if route.RequestMethod == "" && route.RequestMethods == nil {
		fmt.Println("Add route fail please init [RequestMethod|RequestMethods]")
		return errors.New("Add route fail please init [RequestMethod|RequestMethods]")
	}
	if route.Middlewares == nil {
		route.Middlewares = make([]Middleware, 0)
	}
	if httpServerObj.routeTmpParams.Prefix != "" {
		route.Router = httpServerObj.routeTmpParams.Prefix + "/" + route.Router
	}
	if httpServerObj.GlobalMiddleware != nil {
		for _, globalMiddleware := range httpServerObj.GlobalMiddleware {
			route.Middlewares = append(route.Middlewares, globalMiddleware)
		}
	}
	if httpServerObj.routeTmpParams.Middlewares != nil {
		for _, middleware := range httpServerObj.routeTmpParams.Middlewares {
			route.Middlewares = append(route.Middlewares, middleware)
		}
	}
	route.Router = "/" + route.Router
	route.Router = strings.ReplaceAll(route.Router, "//", "/")
	httpServerObj.router = append(httpServerObj.router, route)
	return nil
}

func (httpServerObj *HttpServerObj) Group(middlewares []Middleware, prefix string, routeFun func()) {
	httpServerObj.routeTmpParams = routeTmpParams{Middlewares: middlewares, Prefix: prefix}
	routeFun()
	httpServerObj.routeTmpParams = routeTmpParams{
		Middlewares: []Middleware{},
		Prefix:      "",
	}
}

func (httpServerObj *HttpServerObj) RegisterModule(getHandlerFun func() HttpHandler) HttpServerObj {
	if httpServerObj.modules == nil {
		httpServerObj.modules = make(map[string]func() HttpHandler)
	}
	handler := getHandlerFun()
	tool := ReflectTool{}
	moduleName := handler.GetModuleName()
	methods := tool.GetAllMethods(handler)
	for _, method := range methods {
		if len(method) > 5 && method[0:6] == "Action" {
			action := method[6:len(method)]
			url := moduleName + "/" + action
			url = strings.ReplaceAll(url, "//", "/")
			route := Route{
				RequestMethod: REQUEST_METHOD_ANY,
				Router:        url,
				RunFun: func(ctx *httpContext.HttpContext) interface{} {
					newHandler := getHandlerFun()
					newHandler.SetHttpInfo(ctx)
					res := tool.CallFunByName(newHandler, "Action"+action)
					switch res.(type) {
					case []reflect.Value:
						if len(res.([]reflect.Value)) == 0 {
							return ""
						}
						return res.([]reflect.Value)[0].Interface()
					default:
						return ""
					}
				},
			}
			httpServerObj.AddRoute(route)
		}
	}
	httpServerObj.modules[getHandlerFun().GetModuleName()] = getHandlerFun
	return *httpServerObj
}
